////////////////////////////////////////////////
// DESCRIPTION:
//    Standard application AutoRun 
//
// Legal Notices:
//     Copyright (c) 2008, Telliam Consulting, LLC.
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification,
//     are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//
//     * Neither the name of Telliam Consulting nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// Disclaimer:
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//     IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
//     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
//     OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
//     OF THE POSSIBILITY OF SUCH DAMAGE.  
//

#include <windows.h>
#include <StoreMgr.h>
#include <shlwapi.h>
#include <FusionWare\Win32.h>

static void OnMountedVolume(const PARTINFO& Partition);

using namespace FusionWare::Threads;
using namespace FusionWare::Win32;

///////////////////////////////////////////////////////
// Description: 
//    Processes the device notifications for STORE_MOUNT_GUID
//
// Input: 
//    Dev   - Device Details structure for the device
//    Flags - Flags for the message
//
// Remarks: 
//   Storage Manager sends STORE_MOUNT_GUID notifications
//   with the block driver device name (e.g. "DSK1:") after all 
//   partitions are mounted and initialized with their respective
//   File System Drivers. This function uses that to open the
//   store and enumerate the partitions for the store and calls
//   OnMountedVolume() for each partition found.
//   
// Note: 
//   Dev may indicate the device is detached!
//  
static void OnMountStorage(const DEVDETAIL& Dev, DWORD Flags)
{
    PARTINFO partInfo;
    partInfo.cbSize = sizeof(partInfo);

    // not interested in device removals...
    if(!Dev.fAttached)
        return;

    HANDLE hStore = OpenStore(Dev.szName);
    if(NULL == hStore)
    {
        ERRORMSG(1,(_T("Failed to open store for volume: %s (0x%X)\r\n"),Dev.szName, GetLastError()));
        return;
    }

    HANDLE hFindPart = FindFirstPartition(hStore, &partInfo);
    if(NULL == hFindPart)
    {
        ERRORMSG(1,(_T("ERROR: Failed to locate first partition on volume: %s (0x%X)\r\n"),Dev.szName, GetLastError()));
        return;
    }

    // loop through each partition on the volume
    do {
        OnMountedVolume(partInfo);
    }while(FindNextPartition(hFindPart, &partInfo));

    CloseHandle(hStore);
    FindClosePartition(hFindPart);
}

///////////////////////////////////////////////////////
// Description: 
//    Processes notification of a partion mounted on a 
//    volume.
// 
// Input: 
//    Partion - Partition information for the mounted file system
//
// Remarks: 
//    Constructs the autorun application path and runs it if 
//    the application exists.
//  
//    Microsoft defines the SHGetAutoRunPath but it is only included
//    in the more expensive AYGSHELL enabled builds so that isn't used.
//    Furthermore it implies that there is only one location for autoruns
//    to occur from. (What does it return if there are multiple storage cards?)
//
//    MS also defines that the AutoRun.EXE gets a single paramater "install"
//    on insertion and "uninstall" on removal of the disk. Now this makes sense,
//    or at least at first glance anyway, until one considers that the Autorun.EXE
//    is located on the now removed card... It would be a neat trick indeed if MS
//    had a way to load the exe from the removed card to pass it the "uninstall"
//    paramater! This implementation does not implement that. The implementation
//    of such a trick is left as an excercise for the reader. ;-)
//
static void OnMountedVolume(const PARTINFO& Partition)
{
    TCHAR path[MAX_PATH];
    SYSTEM_INFO sysInfo;

    GetSystemInfo(&sysInfo);
    
    // when the FS is mounted as root there is no volume name
    if(0==Partition.szVolumeName[0])
        _stprintf(path,_T("\\%d\\AutoRun.exe"),sysInfo.dwProcessorType);
    else
        _stprintf(path,_T("\\%s\\%d\\AutoRun.exe"),PathFindFileName(Partition.szVolumeName),sysInfo.dwProcessorType);
    
    if(PathFileExists(path))
    {
        PROCESS_INFORMATION procInfo;
        // enough for '"<full path>" install' plus a little extra for safety
        TCHAR args[MAX_PATH + 16];

        _stprintf(args,_T("\"%s\" install"),path);
    
        if(!CreateProcess(path, args, NULL, NULL, FALSE, 0 ,NULL,NULL, NULL, &procInfo))
            ERRORMSG(1,(_T("AutoRun - CreateProcess(\"%s\",...) FAILED (0x%X)\r\n"),path, GetLastError()));
        else
        {
            // don't need handles so close them to prevent leaks
            CloseHandle(procInfo.hProcess);
            CloseHandle(procInfo.hThread);
        }
    }
}


extern "C" {

    __declspec(dllexport)
    DWORD Init(const TCHAR*)
    {
        static bool Initialized = false;
        
        if(Initialized)
        {
            SetLastError(ERROR_SERVICE_ALREADY_RUNNING);
            return false;
        }

        CDeviceNotifications* pNotif = new CDeviceNotifications;
        if(NULL == pNotif)
        {
            SetLastError(ERROR_OUTOFMEMORY);
            return 0;
        }

        if(!pNotif->StartNotifications(OnMountStorage, STORE_MOUNT_GUID))
            return 0;

        return (DWORD)pNotif;
    }

    __declspec(dllexport)
    BOOL Deinit(CDeviceNotifications* pNotif)
    {
        pNotif->StopNotifications();
        delete pNotif;
        return TRUE;
    }                  

    BOOL WINAPI DllMain(HANDLE hInstDll, DWORD Op, void* pReserved)
    {
        if(Op==DLL_PROCESS_ATTACH)
            DisableThreadLibraryCalls((HMODULE)hInstDll);
         
        return TRUE;
    }
}